package com.nariano.server.domain.entities;

import java.io.Serializable;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

import javax.persistence.AttributeOverrides;
import javax.persistence.AttributeOverride;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinTable;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Transient;

import org.apache.commons.lang.builder.ReflectionToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.springframework.util.Assert;

@Entity
@Table(name="city")
public class City implements Serializable {
    
	@Id
	@Column(name="area_id")
    private Integer id;
    
	@Column(name="name")
    private String name;
	
	@Column(name="owner_user_id")
    private Integer ownerId;
    
	@Embedded
	@AttributeOverrides( {
        @AttributeOverride(name="x", column = @Column(name="location_x") ),
        @AttributeOverride(name="y", column = @Column(name="location_y") )
	} )
    private Location location;
    
	@OneToMany( targetEntity=Population.class, fetch = FetchType.EAGER, cascade=CascadeType.ALL)
    @JoinTable(name="city_population" , 
    	joinColumns=
            @JoinColumn(name="city_id", referencedColumnName="area_id"),
        inverseJoinColumns=
            @JoinColumn(name="population_id", referencedColumnName="population_id")
    )
    private Set<Population> hibernatePopulation = new HashSet<Population>();
    
	@Transient
    private PopulationEntry populationEntry;
    
    @Column(name="create_population_type_id")
    private String hibernateCreateType;
    
    public City() {
    
    }

    public City(String name, Integer owner, Location loc)
    {
    	this(name,owner,loc,PopulationType.PEASENT);
    }
    
    public City(String name, Integer owner, Location loc, PopulationType type) {
        this.name = name;
        this.ownerId = owner;
        setLocation(loc);
        hibernateCreateType = type.getId();
    }

    public City(Integer id) {
		this.id = id;
	}

	public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }
    
    public PopulationType getCreateType() {
		return hibernateCreateType==null?PopulationType.PEASENT:PopulationType.resolve(hibernateCreateType);
	}
    
    public void setCreateType(PopulationType p) {
    	hibernateCreateType = p.getId();
    }

	public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    
    public Integer getOwnerId() {
        return ownerId;
    }

    public void setOwnerId(Integer owner) {
        this.ownerId = owner;
        populationEntry = null;
    }   
    
	public Location getLocation() {
		return location;
	}

	public void setLocation(Location location) {
		this.location = location;
	}

	public Collection<Population> getPopulation() {
        return getPopulationEntry().getPopulations();
    }
	
	public void addPopulation(Population population) {
		getPopulationEntry().addPopulation(population);
		updateHibernatePopulation();
	}
    
    @Override
    public String toString() {
        return ReflectionToStringBuilder.toString(this, ToStringStyle.SHORT_PREFIX_STYLE);
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((id == null) ? 0 : id.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        City other = (City) obj;
        if (id == null) {
            if (other.id != null)
                return false;
        } else if (!id.equals(other.id))
            return false;
        return true;
    }
    
    public int getPopulationCount() {
        return getPopulationEntry().getCount();
    }
    
    public Population findPopulation(PopulationType type) {
        for(Population p : hibernatePopulation) {
            if (p.type().equals(type)){
                return p;
            }
        }
        return null;
    }
    
	private void updateHibernatePopulation() {
		hibernatePopulation.clear();
		hibernatePopulation.addAll(getPopulationEntry().getPopulations());
	}

	public Population moveOutRandomPopulation() {
		Population p = getPopulationEntry().moveOutRandomPopulation();
		updateHibernatePopulation();
		return p;
	}

	private PopulationEntry getPopulationEntry()
	{
		createPopulationEntry();
		return populationEntry;
	}
	
	private void createPopulationEntry() {
    	Assert.notNull(ownerId);
		if (populationEntry==null) {
			if (hibernatePopulation.isEmpty()) {
				populationEntry = new PopulationEntry(ownerId);
			} else {
				populationEntry = new PopulationEntry(ownerId, hibernatePopulation);
			}
			if (populationEntry.cleanOutZeroes()) {
				updateHibernatePopulation();
			}
		}
	}
}
